package org.budo.sso.config;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.budo.sso.BudoStandardSsoFilter;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.spring.bean.factory.support.BeanBuilder;
import org.budo.support.spring.context.aware.RootApplicationContextRefreshedEventListener;
import org.budo.support.spring.util.SpringUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author lmw
 */
@Slf4j
@Getter
@Setter
public class BudoSsoConfigurer extends RootApplicationContextRefreshedEventListener implements BeanDefinitionRegistryPostProcessor {
    private Class<?> ssoFilterType = BudoStandardSsoFilter.class;

    private Boolean registerSignCheckService = true;

    private String appKey;

    private String appSecret;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("#40 postProcessBeanFactory, beanFactory=" + ObjectUtils.identityToString(beanFactory) + ", this=" + ObjectUtils.identityToString(this));
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        log.info("#45 postProcessBeanDefinitionRegistry, registry=" + ObjectUtils.identityToString(registry) + ", this=" + ObjectUtils.identityToString(this));
    }

    /**
     * 奇怪的问题 未回调 onRootApplicationContextRefreshedEvent
     */
    @Override
    protected void onContextRefreshedEvent(final ContextRefreshedEvent contextRefreshedEvent) {
        log.info("#52 onContextRefreshedEvent, contextRefreshedEvent=" + contextRefreshedEvent + ", this=" + ObjectUtils.identityToString(this));

        new Timer().schedule(new TimerTask() {
            public void run() {
                onRootApplicationContextRefreshedEvent(contextRefreshedEvent);
            }
        }, 3000);
    }

    /**
     * 记得好想是 时机问题 不能放在 postProcessBeanFactory 里面执行
     */
    @Override
    protected void onRootApplicationContextRefreshedEvent(ContextRefreshedEvent contextRefreshedEvent) {
        log.info("#63 onRootApplicationContextRefreshedEvent, contextRefreshedEvent=" + contextRefreshedEvent + ", this=" + ObjectUtils.identityToString(this));

        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();

        this.registerSsoFilter(applicationContext);
        this.registerHttpClient(applicationContext);

        this.registerSignCheckService(applicationContext);
    }

    private void registerSsoFilter(ApplicationContext applicationContext) {
        if (SpringUtil.containsBean("ssoFilter")) {
            log.error("#75 registerSsoFilter, containsBean ssoFilter, applicationContext=" + applicationContext);
            return;
        }

        new BeanBuilder() //
                .id("ssoFilter") //
                .type(this.getSsoFilterType()) //
                .registerTo(applicationContext);
    }

    private void registerHttpClient(ApplicationContext applicationContext) {
        String ssoUrl = this.getSsoUrl();

        if (StringUtil.isEmpty(ssoUrl)) {
            log.error("#71 no conf budo.sso.url, no BudoDubboProtocolHttpClient, this=" + ObjectUtils.identityToString(this));
            return;
        }

        if (SpringUtil.containsBean("BudoDubboProtocolHttpClient-sso")) {
            log.error("#94 registerHttpClient containsBean BudoDubboProtocolHttpClient-sso, applicationContext=" + applicationContext);
            return;
        }

        new BeanBuilder() //
                .id("BudoDubboProtocolHttpClient-sso") //
                .type("org.budo.dubbo.protocol.http.client.BudoDubboProtocolHttpClient") //
                .property("dubboReferenceUrl", "budo-" + ssoUrl + "/api") //
                .registerTo(applicationContext);
    }

    protected String getSsoUrl() {
        return SpringUtil.getPropertyValue("budo.sso.url");
    }

    private void registerSignCheckService(ApplicationContext applicationContext) {
        Boolean register = this.getRegisterSignCheckService();
        if (!register) {
            log.error("#88 RegisterSignCheckService=" + register + ", this=" + this);
            return;
        }

        String _appKey = this.getAppKey_1();
        if (StringUtil.isEmpty(_appKey)) {
            log.error("#94 registerSignCheckService return, no conf budo.sso.url, no DefaultSignCheckService");
            return; // 不自动注册了，可能手动注册
        }

        String _appSecret = this.getAppSecret_1();
        if (StringUtil.isEmpty(_appSecret)) { // 有 _appKey 没有 _appSecret 是不正常的
            throw new IllegalArgumentException("#66 appSecret is null, this=" + ObjectUtils.identityToString(this));
        }

        Class<?> type = ReflectUtil.classForName("org.budo.dubbo.protocol.http.sign.SignCheckService");
        List<BeanDefinition> beanDefinitions = SpringUtil.getBeanDefinitionListByInterface(type);
        if (null != beanDefinitions && !beanDefinitions.isEmpty()) {
            log.warn("#106 containsBeanDefinition SignCheckService, beanDefinitions=" + beanDefinitions);
            return;
        }

        if (SpringUtil.containsBean("DefaultSignCheckService-sso")) {
            log.error("#136 registerHttpClient containsBean DefaultSignCheckService-sso, applicationContext=" + applicationContext);
            return;
        }

        new BeanBuilder() //
                .id("DefaultSignCheckService-sso") //
                .type("org.budo.dubbo.protocol.http.sign.DefaultSignCheckService") //
                .property("appKey", _appKey) //
                .property("appSecret", _appSecret) //
                .registerTo(applicationContext);

        log.warn("#117 after register DefaultSignCheckService[appKey=" + appKey + "] to " + applicationContext);
    }

    protected String getAppSecret_1() {
        if (!StringUtil.isNullOrEmpty(this.getAppSecret())) {
            return this.getAppSecret();
        }

        String appSecret = SpringUtil.getPropertyValue("budo.sso.appSecret");
        if (!StringUtil.isNullOrEmpty(appSecret)) {
            return appSecret;
        }

        log.error("#127 no appSecret & budo.sso.appSecret, this=" + ObjectUtils.identityToString(this));
        return null;
    }

    protected String getAppKey_1() {
        if (!StringUtil.isNullOrEmpty(this.getAppKey())) {
            return this.getAppKey();
        }

        String _appKey = SpringUtil.getPropertyValue("budo.sso.appKey");
        if (!StringUtil.isNullOrEmpty(_appKey)) {
            return _appKey;
        }

        log.error("#125 getAppKey_1 return null, no appKey & budo.sso.appKey, this=" + ObjectUtils.identityToString(this));
        return null;
    }
}
